/*
* Copyright 2017 Async-IO.org
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.atmosphere.nettosphere.test;
import com.ning.http.client.AsyncHandler;
import com.ning.http.client.AsyncHttpClient;
import com.ning.http.client.AsyncHttpClientConfig;
import com.ning.http.client.HttpResponseBodyPart;
import com.ning.http.client.HttpResponseHeaders;
import com.ning.http.client.HttpResponseStatus;
import com.ning.http.client.Response;
import com.ning.http.client.ws.WebSocket;
import com.ning.http.client.ws.WebSocketPingListener;
import com.ning.http.client.ws.WebSocketPongListener;
import com.ning.http.client.ws.WebSocketTextListener;
import com.ning.http.client.ws.WebSocketUpgradeHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import org.atmosphere.cpr.ApplicationConfig;
import org.atmosphere.cpr.AtmosphereHandler;
import org.atmosphere.cpr.AtmosphereResource;
import org.atmosphere.cpr.AtmosphereResourceEvent;
import org.atmosphere.cpr.HeaderConfig;
import org.atmosphere.nettosphere.Config;
import org.atmosphere.nettosphere.Handler;
import org.atmosphere.nettosphere.Nettosphere;
import org.atmosphere.websocket.WebSocketEventListenerAdapter;
import org.atmosphere.websocket.WebSocketHandler;
import org.atmosphere.websocket.WebSocketPingPongListener;
import org.atmosphere.websocket.WebSocketProcessor;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import static org.atmosphere.cpr.HeaderConfig.LONG_POLLING_TRANSPORT;
import static org.atmosphere.cpr.HeaderConfig.X_ATMOSPHERE_TRANSPORT;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNull;
public class NettyAtmosphereTest extends BaseTest {
private final static String RESUME = "Resume";
protected int port;
protected Nettosphere server;
private String targetUrl;
private String wsUrl;
@AfterMethod(alwaysRun = true)
public void tearDownGlobal() throws Exception {
server.stop();
}
@BeforeMethod(alwaysRun = true)
public void start() throws IOException {
port = 8080;
targetUrl = "http://127.0.0.1:" + port;
wsUrl = "ws://127.0.0.1:" + port;
}
@Test
public void initParamTest() throws Exception {
final CountDownLatch l = new CountDownLatch(1);
final AtomicBoolean b = new AtomicBoolean(false);
Config config = new Config.Builder()
.port(port)
.host("127.0.0.1")
.initParam("foo", "bar")
.resource("/suspend", new AtmosphereHandler() {
@Override
public void onRequest(AtmosphereResource r) throws IOException {
if (r.getAtmosphereConfig().getInitParameter("foo") != null) {
b.set(true);
}
l.countDown();
}
@Override
public void onStateChange(AtmosphereResourceEvent r) throws IOException {
}
@Override
public void destroy() {
}
}).build();
server = new Nettosphere.Builder().config(config).build();
assertNotNull(server);
server.start();
AsyncHttpClient c = new AsyncHttpClient();
try {
Response r = c.prepareGet(targetUrl + "/suspend").execute().get();
assertEquals(r.getStatusCode(), 200);
assertEquals(b.get(), true);
} finally {
c.close();
}
}
@Test
public void suspendLongPollingTest() throws Exception {
final CountDownLatch l = new CountDownLatch(1);
final CountDownLatch suspendCD = new CountDownLatch(1);
Config config = new Config.Builder()
.port(port)
.host("127.0.0.1")
.resource("/suspend", new AtmosphereHandler() {
private final AtomicBoolean b = new AtomicBoolean(false);
@Override
public void onRequest(AtmosphereResource r) throws IOException {
if (!b.getAndSet(true)) {
r.addEventListener(new WebSocketEventListenerAdapter() {
@Override
public void onSuspend(AtmosphereResourceEvent e) {
suspendCD.countDown();
}
});
r.suspend(-1);
} else {
r.getBroadcaster().broadcast(RESUME);
}
}
@Override
public void onStateChange(AtmosphereResourceEvent r) throws IOException {
if (r.isSuspended()) {
r.getResource().getResponse().getWriter().print(r.getMessage());
r.getResource().resume();
}
}
@Override
public void destroy() {
}
}).build();
server = new Nettosphere.Builder().config(config).build();
assertNotNull(server);
server.start();
final AtomicReference<Response> response = new AtomicReference<Response>();
AsyncHttpClient c = new AsyncHttpClient();
try {
c.prepareGet(targetUrl + "/suspend").setHeader(X_ATMOSPHERE_TRANSPORT, LONG_POLLING_TRANSPORT).execute(new AsyncHandler<Response>() {
final Response.ResponseBuilder b = new Response.ResponseBuilder();
@Override
public void onThrowable(Throwable t) {
l.countDown();
}
@Override
public STATE onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception {
b.accumulate(bodyPart);
return STATE.CONTINUE;
}
@Override
public STATE onStatusReceived(HttpResponseStatus responseStatus) throws Exception {
b.accumulate(responseStatus);
return STATE.CONTINUE;
}
@Override
public STATE onHeadersReceived(HttpResponseHeaders headers) throws Exception {
b.accumulate(headers);
return STATE.CONTINUE;
}
@Override
public Response onCompleted() throws Exception {
response.set(b.build());
l.countDown();
return null;
}
});
suspendCD.await(5, TimeUnit.SECONDS);
Response r = c.prepareGet(targetUrl + "/suspend").execute().get();
assertEquals(r.getStatusCode(), 200);
l.await(5, TimeUnit.SECONDS);
assertEquals(response.get().getStatusCode(), 200);
assertEquals(response.get().getResponseBody().trim(), RESUME);
} finally {
c.close();
}
}
@Test
public void suspendStreamingTest() throws Exception {
final CountDownLatch l = new CountDownLatch(1);
final CountDownLatch suspendCD = new CountDownLatch(1);
Config config = new Config.Builder()
.port(port)
.host("127.0.0.1")
.resource("/suspend", new AtmosphereHandler() {
private final AtomicBoolean suspended = new AtomicBoolean(false);
@Override
public void onRequest(AtmosphereResource r) throws IOException {
if (!suspended.getAndSet(true)) {
r.suspend(-1);
suspendCD.countDown();
} else {
r.getBroadcaster().broadcast(RESUME);
}
}
@Override
public void onStateChange(AtmosphereResourceEvent r) throws IOException {
if (suspended.get()) {
r.getResource().getResponse().getWriter().print(r.getMessage());
r.getResource().resume();
}
}
@Override
public void destroy() {
}
}).build();
server = new Nettosphere.Builder().config(config).build();
assertNotNull(server);
server.start();
final AtomicReference<Response> response = new AtomicReference<Response>();
AsyncHttpClient c = new AsyncHttpClient();
try {
c.prepareGet(targetUrl + "/suspend").setHeader(X_ATMOSPHERE_TRANSPORT, "streaming").execute(new AsyncHandler<Response>() {
final Response.ResponseBuilder b = new Response.ResponseBuilder();
@Override
public void onThrowable(Throwable t) {
l.countDown();
}
@Override
public STATE onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception {
b.accumulate(bodyPart);
return STATE.CONTINUE;
}
@Override
public STATE onStatusReceived(HttpResponseStatus responseStatus) throws Exception {
b.accumulate(responseStatus);
return STATE.CONTINUE;
}
@Override
public STATE onHeadersReceived(HttpResponseHeaders headers) throws Exception {
b.accumulate(headers);
return STATE.CONTINUE;
}
@Override
public Response onCompleted() throws Exception {
response.set(b.build());
l.countDown();
return null;
}
});
suspendCD.await(5, TimeUnit.SECONDS);
Response r = c.prepareGet(targetUrl + "/suspend").execute().get();
assertEquals(r.getStatusCode(), 200);
l.await(5, TimeUnit.SECONDS);
assertEquals(response.get().getStatusCode(), 200);
assertEquals(response.get().getResponseBody().trim(), RESUME);
} finally {
c.close();
}
}
@Test
public void subProtocolTest() throws Exception {
final CountDownLatch l = new CountDownLatch(1);
Config config = new Config.Builder()
.port(port)
.subProtocols("jfa-protocol")
.host("127.0.0.1")
.resource("/suspend", new AtmosphereHandler() {
private final AtomicBoolean b = new AtomicBoolean(false);
@Override
public void onRequest(AtmosphereResource r) throws IOException {
if (!b.getAndSet(true)) {
r.suspend(-1);
} else {
r.getBroadcaster().broadcast(RESUME);
}
}
@Override
public void onStateChange(AtmosphereResourceEvent r) throws IOException {
if (!r.isResuming() || !r.isCancelled()) {
r.getResource().getResponse().getWriter().print(r.getMessage());
r.getResource().resume();
}
}
@Override
public void destroy() {
}
}).build();
server = new Nettosphere.Builder().config(config).build();
assertNotNull(server);
server.start();
final AtomicReference<String> response = new AtomicReference<String>();
AsyncHttpClient c = new AsyncHttpClient();
try {
WebSocket webSocket = c.prepareGet(wsUrl + "/suspend")
.addHeader("Sec-WebSocket-Protocol", "jfa-protocol").execute(new WebSocketUpgradeHandler.Builder().build()).get();
assertNotNull(webSocket);
webSocket.addWebSocketListener(new WebSocketTextListener() {
@Override
public void onMessage(String message) {
response.set(message);
l.countDown();
}
@Override
public void onOpen(WebSocket websocket) {
}
@Override
public void onClose(WebSocket websocket) {
l.countDown();
}
@Override
public void onError(Throwable t) {
l.countDown();
}
}).sendMessage("Ping");
l.await(5, TimeUnit.SECONDS);
webSocket.close();
assertEquals(response.get(), RESUME);
} finally {
c.close();
}
}
@Test
public void suspendWebSocketTest() throws Exception {
final CountDownLatch l = new CountDownLatch(1);
Config config = new Config.Builder()
.port(port)
.host("127.0.0.1")
.resource("/suspend", new AtmosphereHandler() {
private final AtomicBoolean b = new AtomicBoolean(false);
@Override
public void onRequest(AtmosphereResource r) throws IOException {
if (!b.getAndSet(true)) {
r.suspend(-1);
} else {
r.getBroadcaster().broadcast(RESUME);
}
}
@Override
public void onStateChange(AtmosphereResourceEvent r) throws IOException {
if (!r.isResuming() || !r.isCancelled()) {
r.getResource().getResponse().getWriter().print(r.getMessage());
r.getResource().resume();
}
}
@Override
public void destroy() {
}
}).build();
server = new Nettosphere.Builder().config(config).build();
assertNotNull(server);
server.start();
final AtomicReference<String> response = new AtomicReference<String>();
AsyncHttpClient c = new AsyncHttpClient();
try {
WebSocket webSocket = c.prepareGet(wsUrl + "/suspend").execute(new WebSocketUpgradeHandler.Builder().build()).get();
assertNotNull(webSocket);
webSocket.addWebSocketListener(new WebSocketTextListener() {
@Override
public void onMessage(String message) {
response.set(message);
l.countDown();
}
@Override
public void onOpen(WebSocket websocket) {
}
@Override
public void onClose(WebSocket websocket) {
l.countDown();
}
@Override
public void onError(Throwable t) {
l.countDown();
}
}).sendMessage("Ping");
l.await(5, TimeUnit.SECONDS);
webSocket.close();
assertEquals(response.get(), RESUME);
} finally {
c.close();
}
}
@Test
public void webSocketHandlerTest() throws Exception {
final CountDownLatch l = new CountDownLatch(1);
final AtomicBoolean handshake = new AtomicBoolean(true);
Config config = new Config.Builder()
.port(port)
.host("127.0.0.1")
.resource(new Handler() {
@Override
public void handle(AtmosphereResource r) {
if (!handshake.getAndSet(false)) {
r.getResponse().write("Hello World from Nettosphere");
}
}
}).build();
server = new Nettosphere.Builder().config(config).build();
assertNotNull(server);
server.start();
final AtomicReference<String> response = new AtomicReference<String>();
AsyncHttpClient c = new AsyncHttpClient();
try {
WebSocket webSocket = c.prepareGet(wsUrl).execute(new WebSocketUpgradeHandler.Builder().build()).get();
assertNotNull(webSocket);
webSocket.addWebSocketListener(new WebSocketTextListener() {
@Override
public void onMessage(String message) {
response.set(message);
l.countDown();
}
@Override
public void onOpen(WebSocket websocket) {
}
@Override
public void onClose(WebSocket websocket) {
}
@Override
public void onError(Throwable t) {
}
});
webSocket.sendMessage("Hello World from Nettosphere");
l.await(5, TimeUnit.SECONDS);
webSocket.close();
assertEquals(response.get(), "Hello World from Nettosphere");
} finally {
c.close();
}
}
@Test
public void textFrameAsBinaryTest() throws Exception {
final CountDownLatch l = new CountDownLatch(1);
final AtomicBoolean handshake = new AtomicBoolean(true);
Config config = new Config.Builder()
.port(port)
.host("127.0.0.1")
.textFrameAsBinary(true)
.resource(new Handler() {
@Override
public void handle(AtmosphereResource r) {
if (!handshake.getAndSet(false))
r.getResponse().write(r.getRequest().body().asBytes()).closeStreamOrWriter();
}
}).build();
server = new Nettosphere.Builder().config(config).build();
assertNotNull(server);
server.start();
final AtomicReference<String> response = new AtomicReference<String>();
AsyncHttpClient c = new AsyncHttpClient();
try {
WebSocket webSocket = c.prepareGet(wsUrl).execute(new WebSocketUpgradeHandler.Builder().build()).get();
assertNotNull(webSocket);
webSocket.addWebSocketListener(new WebSocketTextListener() {
@Override
public void onMessage(String message) {
response.set(message);
l.countDown();
}
@Override
public void onOpen(WebSocket websocket) {
}
@Override
public void onClose(WebSocket websocket) {
}
@Override
public void onError(Throwable t) {
}
});
webSocket.sendMessage("Hello World from Nettosphere");
l.await(5, TimeUnit.SECONDS);
webSocket.close();
assertEquals(response.get(), "Hello World from Nettosphere");
} finally {
c.close();
}
}
@Test
public void httpHandlerTest() throws Exception {
Config config = new Config.Builder()
.port(port)
.host("127.0.0.1")
.resource(new Handler() {
@Override
public void handle(AtmosphereResource r) {
r.getResponse().write("Hello World from Nettosphere").closeStreamOrWriter();
}
}).build();
server = new Nettosphere.Builder().config(config).build();
assertNotNull(server);
server.start();
AsyncHttpClient c = new AsyncHttpClient();
try {
Response response = c.prepareGet(targetUrl).execute().get();
assertNotNull(response);
assertEquals(response.getResponseBody(), "Hello World from Nettosphere");
} finally {
c.close();
}
}
@Test
public void httspHandlerTest() throws Exception {
final SSLContext sslContext = createSSLContext();
final String cipherSuiteForTestingOnly_WEAK_butPreinstalled = "TLS_ECDH_anon_WITH_AES_128_CBC_SHA";
Config config = new Config.Builder()
.port(port)
.host("127.0.0.1")
.sslContext(sslContext)
.enabledCipherSuites(new String[]{cipherSuiteForTestingOnly_WEAK_butPreinstalled})
.resource(new Handler() {
@Override
public void handle(AtmosphereResource r) {
r.getResponse().write("Hello World from Nettosphere").closeStreamOrWriter();
}
}).build();
server = new Nettosphere.Builder().config(config).build();
assertNotNull(server);
server.start();
AsyncHttpClient c = new AsyncHttpClient(new AsyncHttpClientConfig.Builder()
.setEnabledCipherSuites(new String[]{cipherSuiteForTestingOnly_WEAK_butPreinstalled})
.setAcceptAnyCertificate(true)
.build());
try {
Response response = c.prepareGet("https://127.0.0.1:" + port).execute().get();
assertNotNull(response);
assertEquals(response.getResponseBody(), "Hello World from Nettosphere");
} finally {
c.close();
}
}
@Test
public void nettySslContextTest() throws Exception {
final CountDownLatch l = new CountDownLatch(1);
SelfSignedCertificate ssc = new SelfSignedCertificate();
SslContext sslCtx = SslContext.newServerContext(ssc.certificate(), ssc.privateKey());
Config config = new Config.Builder()
.port(port)
.host("127.0.0.1")
.sslContext(sslCtx)
.resource(new Handler() {
@Override
public void handle(AtmosphereResource r) {
r.getResponse().write("Hello World from Nettosphere").closeStreamOrWriter();
}
}).build();
server = new Nettosphere.Builder().config(config).build();
assertNotNull(server);
server.start();
AsyncHttpClient c = new AsyncHttpClient(new AsyncHttpClientConfig.Builder()
.setAcceptAnyCertificate(true)
.build());
try {
final AtomicReference<String> response = new AtomicReference<String>();
WebSocket webSocket = c.prepareGet("wss://127.0.0.1:" + port).execute(new WebSocketUpgradeHandler.Builder().build()).get();
assertNotNull(webSocket);
webSocket.addWebSocketListener(new WebSocketTextListener() {
@Override
public void onMessage(String message) {
response.set(message);
l.countDown();
}
@Override
public void onOpen(WebSocket websocket) {
}
@Override
public void onClose(WebSocket websocket) {
}
@Override
public void onError(Throwable t) {
}
});
l.await(5, TimeUnit.SECONDS);
webSocket.close();
assertEquals(response.get(), "Hello World from Nettosphere");
} finally {
c.close();
}
}
@Test
public void wssHandlerTest() throws Exception {
final CountDownLatch l = new CountDownLatch(1);
final SSLContext sslContext = createSSLContext();
final String cipherSuiteForTestingOnly_WEAK_butPreinstalled = "TLS_ECDH_anon_WITH_AES_128_CBC_SHA";
Config config = new Config.Builder()
.port(port)
.host("127.0.0.1")
.sslContext(sslContext)
.enabledCipherSuites(new String[]{cipherSuiteForTestingOnly_WEAK_butPreinstalled})
.resource(new Handler() {
@Override
public void handle(AtmosphereResource r) {
r.getResponse().write("Hello World from Nettosphere").closeStreamOrWriter();
}
}).build();
server = new Nettosphere.Builder().config(config).build();
assertNotNull(server);
server.start();
AsyncHttpClient c = new AsyncHttpClient(new AsyncHttpClientConfig.Builder()
.setAcceptAnyCertificate(true)
.setEnabledCipherSuites(new String[]{cipherSuiteForTestingOnly_WEAK_butPreinstalled})
.build());
try {
final AtomicReference<String> response = new AtomicReference<String>();
WebSocket webSocket = c.prepareGet("wss://127.0.0.1:" + port).execute(new WebSocketUpgradeHandler.Builder().build()).get();
assertNotNull(webSocket);
webSocket.addWebSocketListener(new WebSocketTextListener() {
@Override
public void onMessage(String message) {
response.set(message);
l.countDown();
}
@Override
public void onOpen(WebSocket websocket) {
}
@Override
public void onClose(WebSocket websocket) {
}
@Override
public void onError(Throwable t) {
}
});
l.await(5, TimeUnit.SECONDS);
webSocket.close();
assertEquals(response.get(), "Hello World from Nettosphere");
} finally {
c.close();
}
}
private static SSLContext createSSLContext() {
try {
InputStream keyStoreStream = BaseTest.class.getResourceAsStream("ssltest-cacerts.jks");
char[] keyStorePassword = "changeit".toCharArray();
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(keyStoreStream, keyStorePassword);
// Set up key manager factory to use our key store
char[] certificatePassword = "changeit".toCharArray();
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, certificatePassword);
// Initialize the SSLContext to work with our key managers.
KeyManager[] keyManagers = kmf.getKeyManagers();
TrustManager[] trustManagers = new TrustManager[]{DUMMY_TRUST_MANAGER};
SecureRandom secureRandom = new SecureRandom();
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, trustManagers, secureRandom);
return sslContext;
} catch (Exception e) {
throw new Error("Failed to initialize SSLContext", e);
}
}
private static final AtomicBoolean TRUST_SERVER_CERT = new AtomicBoolean(true);
private static final TrustManager DUMMY_TRUST_MANAGER = new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
if (!TRUST_SERVER_CERT.get()) {
throw new CertificateException("Server certificate not trusted.");
}
}
};
@Test
public void closeTest() throws Exception {
final CountDownLatch l = new CountDownLatch(1);
final CountDownLatch suspendCD = new CountDownLatch(1);
Config config = new Config.Builder()
.port(port)
.host("127.0.0.1")
.resource("/suspend", new AtmosphereHandler() {
@Override
public void onRequest(AtmosphereResource r) throws IOException {
r.addEventListener(new WebSocketEventListenerAdapter() {
@Override
public void onSuspend(AtmosphereResourceEvent e) {
suspendCD.countDown();
}
});
r.suspend(60, TimeUnit.SECONDS);
}
@Override
public void onStateChange(AtmosphereResourceEvent r) throws IOException {
}
@Override
public void destroy() {
}
}).build();
server = new Nettosphere.Builder().config(config).build();
assertNotNull(server);
server.start();
final AtomicReference<HttpResponseHeaders> response = new AtomicReference<HttpResponseHeaders>();
AsyncHttpClient c = new AsyncHttpClient();
try {
c.prepareGet(targetUrl + "/suspend?" + X_ATMOSPHERE_TRANSPORT + "=" + HeaderConfig.LONG_POLLING_TRANSPORT).execute(new AsyncHandler<Response>() {
@Override
public void onThrowable(Throwable t) {
l.countDown();
}
@Override
public STATE onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception {
return STATE.CONTINUE;
}
@Override
public STATE onStatusReceived(HttpResponseStatus responseStatus) throws Exception {
return STATE.CONTINUE;
}
@Override
public STATE onHeadersReceived(HttpResponseHeaders headers) throws Exception {
response.set(headers);
l.countDown();
return STATE.ABORT;
}
@Override
public Response onCompleted() throws Exception {
return null;
}
});
suspendCD.await(5, TimeUnit.SECONDS);
Thread.sleep(2000);
server.stop();
l.await(20, TimeUnit.SECONDS);
assertNotNull(response.get());
} finally {
c.close();
}
}
@Test
public void suspendNoChunkStreamingTest() throws Exception {
final CountDownLatch l = new CountDownLatch(1);
final CountDownLatch suspendCD = new CountDownLatch(1);
Config config = new Config.Builder()
.port(port)
.host("127.0.0.1")
.supportChunking(false)
.resource("/suspend", new AtmosphereHandler() {
private final AtomicBoolean suspended = new AtomicBoolean(false);
@Override
public void onRequest(AtmosphereResource r) throws IOException {
if (!suspended.getAndSet(true)) {
r.suspend(-1);
suspendCD.countDown();
} else {
r.getBroadcaster().broadcast(RESUME);
}
}
@Override
public void onStateChange(AtmosphereResourceEvent r) throws IOException {
if (suspended.get()) {
r.getResource().getResponse().getWriter().print(r.getMessage());
r.getResource().resume();
}
}
@Override
public void destroy() {
}
}).build();
server = new Nettosphere.Builder().config(config).build();
assertNotNull(server);
server.start();
final AtomicReference<Response> response = new AtomicReference<Response>();
AsyncHttpClient c = new AsyncHttpClient();
try {
c.prepareGet(targetUrl + "/suspend").setHeader(X_ATMOSPHERE_TRANSPORT, "streaming").execute(new AsyncHandler<Response>() {
final Response.ResponseBuilder b = new Response.ResponseBuilder();
@Override
public void onThrowable(Throwable t) {
l.countDown();
}
@Override
public STATE onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception {
b.accumulate(bodyPart);
return STATE.CONTINUE;
}
@Override
public STATE onStatusReceived(HttpResponseStatus responseStatus) throws Exception {
b.accumulate(responseStatus);
return STATE.CONTINUE;
}
@Override
public STATE onHeadersReceived(HttpResponseHeaders headers) throws Exception {
b.accumulate(headers);
return STATE.CONTINUE;
}
@Override
public Response onCompleted() throws Exception {
response.set(b.build());
l.countDown();
return null;
}
});
suspendCD.await(5, TimeUnit.SECONDS);
Response r = c.prepareGet(targetUrl + "/suspend").execute().get();
assertEquals(r.getStatusCode(), 200);
l.await(5, TimeUnit.SECONDS);
assertEquals(response.get().getStatusCode(), 200);
assertEquals(response.get().getResponseBody().trim(), RESUME);
} finally {
c.close();
}
}
@Test
public void aggregatePostInMemoryTest() throws Exception {
final CountDownLatch l = new CountDownLatch(1);
final CountDownLatch suspendCD = new CountDownLatch(1);
Config config = new Config.Builder()
.port(port)
.host("127.0.0.1")
.supportChunking(false)
.aggregateRequestBodyInMemory(false)
.resource("/suspend", new AtmosphereHandler() {
private final AtomicBoolean suspended = new AtomicBoolean(false);
@Override
public void onRequest(AtmosphereResource r) throws IOException {
if (!suspended.getAndSet(true)) {
r.suspend(-1);
suspendCD.countDown();
} else {
r.getBroadcaster().broadcast(new String(r.getRequest().body().asBytes()));
}
}
@Override
public void onStateChange(AtmosphereResourceEvent r) throws IOException {
if (suspended.get()) {
r.getResource().getResponse().getWriter().print(r.getMessage());
if (r.getMessage().toString().contains("message")) {
r.getResource().resume();
}
}
}
@Override
public void destroy() {
}
}).build();
server = new Nettosphere.Builder().config(config).build();
assertNotNull(server);
server.start();
final AtomicReference<Response> response = new AtomicReference<Response>();
AsyncHttpClient c = new AsyncHttpClient();
try {
c.prepareGet(targetUrl + "/suspend").setHeader(X_ATMOSPHERE_TRANSPORT, "streaming").execute(new AsyncHandler<Response>() {
final Response.ResponseBuilder b = new Response.ResponseBuilder();
@Override
public void onThrowable(Throwable t) {
l.countDown();
}
@Override
public STATE onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception {
b.accumulate(bodyPart);
return STATE.CONTINUE;
}
@Override
public STATE onStatusReceived(HttpResponseStatus responseStatus) throws Exception {
b.accumulate(responseStatus);
return STATE.CONTINUE;
}
@Override
public STATE onHeadersReceived(HttpResponseHeaders headers) throws Exception {
b.accumulate(headers);
return STATE.CONTINUE;
}
@Override
public Response onCompleted() throws Exception {
response.set(b.build());
l.countDown();
return null;
}
});
suspendCD.await(5, TimeUnit.SECONDS);
StringBuilder b = new StringBuilder();
for (int i = 0; i < 10000; i++) {
b.append("======");
}
b.append("message");
Response r = c.preparePost(targetUrl + "/suspend").setContentLength(b.toString().length()).setBody(b.toString()).execute().get();
assertEquals(r.getStatusCode(), 200);
l.await(5, TimeUnit.SECONDS);
assertEquals(response.get().getStatusCode(), 200);
assertEquals(response.get().getResponseBody().trim(), b.toString());
} finally {
c.close();
}
}
@Test
public void chunkPostInMemoryTest() throws Exception {
final CountDownLatch l = new CountDownLatch(1);
final CountDownLatch suspendCD = new CountDownLatch(1);
Config config = new Config.Builder()
.port(port)
.host("127.0.0.1")
.supportChunking(true)
.aggregateRequestBodyInMemory(false)
.resource("/suspend", new AtmosphereHandler() {
private final AtomicBoolean suspended = new AtomicBoolean(false);
@Override
public void onRequest(AtmosphereResource r) throws IOException {
if (!suspended.getAndSet(true)) {
r.suspend(-1);
suspendCD.countDown();
} else {
r.getBroadcaster().broadcast(new String(r.getRequest().body().asBytes()));
}
}
@Override
public void onStateChange(AtmosphereResourceEvent r) throws IOException {
if (r.isSuspended()) {
r.getResource().getResponse().getWriter().print(r.getMessage());
if (r.getMessage().toString().contains("message")) {
r.getResource().resume();
}
}
}
@Override
public void destroy() {
}
}).build();
server = new Nettosphere.Builder().config(config).build();
assertNotNull(server);
server.start();
final AtomicReference<Response> response = new AtomicReference<Response>();
AsyncHttpClient c = new AsyncHttpClient();
try {
c.prepareGet(targetUrl + "/suspend").setHeader(X_ATMOSPHERE_TRANSPORT, "streaming").execute(new AsyncHandler<Response>() {
final Response.ResponseBuilder b = new Response.ResponseBuilder();
@Override
public void onThrowable(Throwable t) {
l.countDown();
}
@Override
public STATE onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception {
b.accumulate(bodyPart);
return STATE.CONTINUE;
}
@Override
public STATE onStatusReceived(HttpResponseStatus responseStatus) throws Exception {
b.accumulate(responseStatus);
return STATE.CONTINUE;
}
@Override
public STATE onHeadersReceived(HttpResponseHeaders headers) throws Exception {
b.accumulate(headers);
return STATE.CONTINUE;
}
@Override
public Response onCompleted() throws Exception {
response.set(b.build());
l.countDown();
return null;
}
});
suspendCD.await(5, TimeUnit.SECONDS);
StringBuilder b = new StringBuilder();
for (int i = 0; i < 10000; i++) {
b.append("======");
}
b.append("message");
Response r = c.preparePost(targetUrl + "/suspend").setContentLength(b.toString().length()).setBody(b.toString()).execute().get();
assertEquals(r.getStatusCode(), 200);
l.await(5, TimeUnit.SECONDS);
assertEquals(response.get().getStatusCode(), 200);
assertEquals(response.get().getResponseBody().trim(), b.toString());
} finally {
c.close();
}
}
public final static class PingPongHandler implements WebSocketHandler, WebSocketPingPongListener {
@Override
public void onByteMessage(org.atmosphere.websocket.WebSocket webSocket, byte[] data, int offset, int length) throws IOException {
}
@Override
public void onTextMessage(org.atmosphere.websocket.WebSocket webSocket, String data) throws IOException {
}
@Override
public void onOpen(org.atmosphere.websocket.WebSocket webSocket) throws IOException {
}
@Override
public void onClose(org.atmosphere.websocket.WebSocket webSocket) {
}
@Override
public void onError(org.atmosphere.websocket.WebSocket webSocket, WebSocketProcessor.WebSocketException t) {
}
@Override
public void onPong(org.atmosphere.websocket.WebSocket webSocket, byte[] bytes, int i, int i1) {
webSocket.sendPing("Hello from server".getBytes());
}
@Override
public void onPing(org.atmosphere.websocket.WebSocket webSocket, byte[] bytes, int i, int i1) {
webSocket.sendPong("Hello from server".getBytes());
}
}
@Test
public void pingTest() throws Exception {
final CountDownLatch l = new CountDownLatch(1);
Config config = new Config.Builder()
.port(port)
.host("127.0.0.1")
.resource("/pingPong", PingPongHandler.class).build();
server = new Nettosphere.Builder().config(config).build();
assertNotNull(server);
server.start();
final AtomicReference<String> response = new AtomicReference<String>();
AsyncHttpClient c = new AsyncHttpClient();
try {
WebSocket webSocket = c.prepareGet(wsUrl + "/pingPong").execute(new WebSocketUpgradeHandler.Builder().build()).get();
assertNotNull(webSocket);
webSocket.addWebSocketListener(new WebSocketPongListener() {
@Override
public void onPong(byte[] message) {
response.set(new String(message));
l.countDown();
}
@Override
public void onOpen(WebSocket websocket) {
}
@Override
public void onClose(WebSocket websocket) {
}
@Override
public void onError(Throwable t) {
}
}).sendPing("Hello from Client".getBytes());
l.await(5, TimeUnit.SECONDS);
webSocket.close();
assertEquals(response.get(), "Hello from server");
} finally {
c.close();
}
}
@Test
public void pongTest() throws Exception {
final CountDownLatch l = new CountDownLatch(1);
Config config = new Config.Builder()
.port(port)
.host("127.0.0.1")
.resource("/pingPong", PingPongHandler.class).build();
server = new Nettosphere.Builder().config(config).build();
assertNotNull(server);
server.start();
final AtomicReference<String> response = new AtomicReference<String>();
AsyncHttpClient c = new AsyncHttpClient();
try {
WebSocket webSocket = c.prepareGet(wsUrl + "/pingPong").execute(new WebSocketUpgradeHandler.Builder().build()).get();
assertNotNull(webSocket);
webSocket.addWebSocketListener(new WebSocketPingListener() {
@Override
public void onPing(byte[] message) {
response.set(new String(message));
l.countDown();
}
@Override
public void onOpen(WebSocket websocket) {
}
@Override
public void onClose(WebSocket websocket) {
}
@Override
public void onError(Throwable t) {
}
}).sendPong("Hello from Client".getBytes());
l.await(5, TimeUnit.SECONDS);
webSocket.close();
assertEquals(response.get(), "Hello from server");
} finally {
c.close();
}
}
@Test
public void timeoutTest() throws Exception {
final CountDownLatch l = new CountDownLatch(1);
Config config = new Config.Builder()
.port(port)
.initParam(ApplicationConfig.WEBSOCKET_IDLETIME, "5000")
.subProtocols("jfa-protocol")
.host("127.0.0.1")
.resource("/suspend", new AtmosphereHandler() {
private final AtomicBoolean b = new AtomicBoolean(false);
@Override
public void onRequest(AtmosphereResource r) throws IOException {
r.suspend(-1);
}
@Override
public void onStateChange(AtmosphereResourceEvent r) throws IOException {
}
@Override
public void destroy() {
}
}).build();
server = new Nettosphere.Builder().config(config).build();
assertNotNull(server);
server.start();
final AtomicReference<String> response = new AtomicReference<String>();
AsyncHttpClient c = new AsyncHttpClient();
try {
WebSocket webSocket = c.prepareGet(wsUrl + "/suspend")
.addHeader("Sec-WebSocket-Protocol", "jfa-protocol").execute(new WebSocketUpgradeHandler.Builder().build()).get();
assertNotNull(webSocket);
webSocket.addWebSocketListener(new WebSocketTextListener() {
@Override
public void onMessage(String message) {
response.set(message);
l.countDown();
}
@Override
public void onOpen(WebSocket websocket) {
}
@Override
public void onClose(WebSocket websocket) {
l.countDown();
}
@Override
public void onError(Throwable t) {
}
});
l.await(10, TimeUnit.SECONDS);
assertNull(response.get());
assertEquals(l.getCount(), 0);
} finally {
c.close();
}
}
}